package bagging;

import java.util.ArrayList;
import java.util.List;

import weka.classifiers.Classifier;
import weka.core.Instance;
import weka.core.Instances;
import weka.filters.unsupervised.instance.Randomize;

public class Bagging {
	
	private Classifier[] classifiers = null;
	private Classifier baseClassifier = null;
	private int numberBaseClassifiers = 5; //default value
	private Instances trainingDataset = null;
	private int numberOfClasses = 0;
	
	
	public Bagging(Instances trainingDataset, Classifier baseClassifier, int numberBaseClassifiers, int numberOfClasses){
		this.baseClassifier = baseClassifier;
		this.numberBaseClassifiers = numberBaseClassifiers;
		this.trainingDataset = trainingDataset;
		this.numberOfClasses = numberOfClasses;
	}
	
	public void buildBagging(int percentSplit){
		try{
			this.trainingBaseClassifier(percentSplit);
		}catch(Exception e){
			System.out.println("void buildBagging(percentSplit): error while building Bagging");
			e.printStackTrace();
		}
	}
	/*
	 * Similar ao método do arquivo que encontramos. para utiliza-lo basta remover o comentário
	 * e comentar o próximo método. comparar os resultados dos dois métodos.
	 */
/*	public void trainingBaseClassifier(int percentSplit) throws Exception {
		Instances baggDataTraining = null;
		int i = 0, j = 0;
		int percentageData = (int)(trainingDataset.numInstances() * percentSplit / 100);
		classifiers = Classifier.makeCopies(baseClassifier, numberBaseClassifiers);
	
		Random random = new Random();
		for (j = 0; j < classifiers.length; j++) {
			baggDataTraining = new Instances(trainingDataset, percentageData);
			while (baggDataTraining.numInstances() < percentageData) {
				i = (int) (random.nextDouble() * (double) trainingDataset.numInstances());
				baggDataTraining.add(trainingDataset.instance(i));
			}
			classifiers[j].buildClassifier(baggDataTraining);
		}
	}*/
	
	public void trainingBaseClassifier(double percentSplit) throws Exception {
		int trainSize = (int) (this.trainingDataset.numInstances()* percentSplit / 100);
		Instances sampledDataset = null;
		classifiers = Classifier.makeCopies(baseClassifier, numberBaseClassifiers);

		for (int i = 0; i < classifiers.length; i++) {
			Randomize randomize = new weka.filters.unsupervised.instance.Randomize();
			randomize.setRandomSeed(new java.util.Random().nextInt(99) + 1);
			weka.filters.Filter random = randomize;
			random.setInputFormat(trainingDataset);
			sampledDataset = weka.filters.Filter.useFilter(trainingDataset,	random);
			Instances train = new Instances(sampledDataset, 0, trainSize);
			classifiers[i].buildClassifier(train);
		}
	}
	
	
	// executa classificador
	public int classifyInstance(Instance instance){
		// guardar as predicoes
		List<Double> predicoes = new ArrayList<Double>();
		
		for(int i = 0; i < classifiers.length; i++){			
			try {
				predicoes.add(classifiers[i].classifyInstance(instance));
			} catch (Exception e) {
				System.out.println("classifyInstance: error while classifiy instance");
				e.printStackTrace();
			}			
		}
			
		return majorityVoting(predicoes,this.numberOfClasses);
	}
	
	public int majorityVoting(List<Double> predicoes, int numberOfClasses){
		Double [][] decisionProfile = new Double[classifiers.length][numberOfClasses];
		
		for(int i = 0; i < numberOfClasses; i++){
			for (int j = 0; j < classifiers.length; j++) {
				decisionProfile[j][i] = 0.0;
			}
		}
		
		for (int j = 0; j < classifiers.length; j++) {
			decisionProfile[j][predicoes.get(j).intValue()] = 1.0;
		}
		
		Double [] prediction = new Double[numberOfClasses];
		
		for(int i = 0; i < numberOfClasses; i++){
			prediction[i] = 0.0;
		}
		
		for(int i = 0; i < numberOfClasses; i++){
			for(int j = 0; j < classifiers.length; j++){
				prediction[i] += decisionProfile[j][i]; 
			}
		}
		
		Double max = prediction[0];
		int indexMax = 0;
		for(int i = 1; i < numberOfClasses; i++){
			if(max < prediction[i]){
				max = prediction[i];
				indexMax = i;
			}
		}
		
		return indexMax;
	}
	
	public Classifier[] getBaseClassifiers(){
		return classifiers;
	}
}